﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using EAS_Protocol.Helpers;
using EAS_Protocol.Models;
using EAS_Protocol;
using System.IO;
using System.Xml;
using System.Text;
using System.Xml.XPath;

namespace EAS_Web_API.Controllers
{
    /// <summary>
    /// API for manipulating the "Notes" folder.
    /// </summary>
    public class NotesController : ApiController
    {
        /// <summary>        
        /// Syncs all Notes in the "Default Notes" folder.
        /// </summary>
        /// <param name="serverAddress">The address of the Exchange ActiveSync Server. Should be in the form "http(s)://FQDN"</param>
        /// <param name="credentials">Credentials for an EAS user. Format => base64(username:password).</param>
        /// <param name="DeviceID">The unique id of the "device".</param>
        /// <param name="DeviceType">The type of the "device".</param>
        /// <returns>List&lt;Notes&gt;</returns>
        [HttpGet]
        [AllowAnonymous]
        public IHttpActionResult syncNotes(string serverAddress, string credentials, string DeviceID, string DeviceType)
        {
            //The algorithm for syncing notes is a three-step process:
            // - Do a FolderSync to get the Ids for all folders, and identify the folder for Default notes
            // - Do a Sync with a SyncKey == 0 to get a new SyncKey
            // - Do a Sync with the new SyncKey

            List<Note> notes = new List<Note>();

            string strUsername = HelperMethods.getUsername(credentials);
            string strDeviceID = DeviceID;
            string strDeviceType = DeviceType;
            string creds = credentials;

            string easUrl = serverAddress;

            string syncKey = "0";
            string collectionId = "";

            string ASVersion;
            string UserAgent;

            //Filter out necessary values from headers
            //Fall back to defaults if not provided
            //To do : Should defaults be provided or should the API throw an error?
            var headerValues = new List<string>().AsEnumerable();
            if (Request.Headers.TryGetValues("MS-ASProtocolVersion", out headerValues))
            {
                ASVersion = headerValues.First().ToString();
            }
            else
            {
                ASVersion = "14.1";
            }
            if (Request.Headers.TryGetValues("DeviceUserAgent", out headerValues))
            {
                UserAgent = headerValues.First().ToString();
            }
            else
            {
                UserAgent = "EASWeb";
            }

            Device dev = new Device();
            dev.ASVersion = ASVersion;
            dev.DeviceID = DeviceID;
            dev.DeviceType = DeviceType;
            dev.UserAgent = UserAgent;
            dev.Username = strUsername;
            dev.ServerAddress = easUrl;
            dev.Credentials = credentials;

            string fsResponse = AS_CMD.FolderSync(dev);
            collectionId = AS_CMD.GetCollectionID(fsResponse, FolderSyncTypes.Default_Notes);
            syncKey = AS_CMD.Sync(dev, collectionId);
            string syncResponse = AS_CMD.Sync(dev, syncKey, collectionId);             

            try
            {
                //We parse out the note elements
                var xmlSync = new XmlDocument();
                xmlSync.LoadXml(syncResponse);

                //Check the status of the sync request
                var status = xmlSync.GetElementsByTagName("Status");

                //Status of "1" == Success
                if (status[0].InnerXml == "1")
                {
                    var noteResults = xmlSync.GetElementsByTagName("ApplicationData");
                    int noteItems = noteResults.Count;

                    #region
                    for (int i = 0; i < noteItems; i++)
                    {
                        notes.Add(new Note());
                        //A given note may have empty/null properties 
                        //These tags will not be included in the XML
                        //so we need to check the count for each xmlItem before adding the value.
                        //Note that the default is to truncate the entries meaning that not all properties are included
                        //This means there are properties in the model having a null value

                        var appDataNodes = noteResults.Item(i).ChildNodes;

                        for (int j = 0; j < appDataNodes.Count; j++)
                        {
                            var itemName = appDataNodes.Item(j).Name;
                            var itemValue = appDataNodes.Item(j).InnerText;

                            if (itemName == "Subject")
                                notes[i].Subject = itemValue;
                            if (itemName == "MessageClass")
                                notes[i].MessageClass = itemValue;
                            if (itemName == "LastModifiedDate")
                                notes[i].LastModifiedDate = itemValue;
                            //Note: only handles one category at the moment.
                            if (itemName == "Categories")
                                notes[i].Category = itemValue;
                        }                                      
                    }
                    #endregion

                }
                //Status !1 == !OK
                else
                {
                    //ToDo: work the switch case into a lookup to some common status code handling.

                    //http://msdn.microsoft.com/en-us/library/gg675457(v=exchg.80).aspx
                    switch (status[0].InnerXml)
                    {
                        //Invalid synchronization key.
                        case "3":
                            break;

                        //Protocol error
                        case "4":
                            break;

                        //Server error
                        case "5":
                            break;

                        //Error in client/server conversion
                        case "6":
                            break;

                        //Conflict matching the client and server object
                        case "7":
                            break;

                        //Object not found
                        case "8":
                            break;

                        //The Sync command cannot be completed
                        case "9":
                            break;

                        //The folder hierarchy has changed
                        case "12":
                            break;

                        //The Sync command request is not complete
                        case "13":
                            break;

                        //Invalid Wait or HeartbeatInterval value
                        case "14":
                            break;

                        //Invalid Sync command request
                        case "15":
                            break;

                        //Retry
                        case "16":
                            break;

                        default:
                            break;
                    }
                }
            }
            catch (Exception)
            {
                return InternalServerError();
            }
            //#endregion

            if (notes.Count == 0)
                return StatusCode(HttpStatusCode.NoContent);
            else
                return Ok(notes);
        }

    }
}
